
#[derive(Clone, Debug, PartialEq)]
pub enum ExpToken {
    Opchar(char),
    Opnum(f64),
}

#[derive(Debug)]
pub struct Expression {
    val: Vec<ExpToken>,
    index: u32,
}


impl Expression {
    pub fn new(exp: String) -> Result<Expression, String> {
        let exp_vec = parse_exp(exp)?;
        Ok(Expression {
            val: exp_vec,
            index: 0,
        })
    }

    pub fn get(&self) -> &Vec<ExpToken> {
        &self.val
    }

    pub fn get_el(&mut self) -> Option<ExpToken> {
        let mut token: Option<ExpToken> = None;
        match self.val.get(self.index as usize) {
            Some(x) => {
                token = Some(x.clone());
            },
            None => {
                token = None;
            },
        }
        self.index = self.index + 1;
        return token;
    }
}

fn parse_exp(s: String) -> Result<Vec<ExpToken>, String> {

    #[derive(PartialEq)]
    enum LastOpType {
        Init,
        Op,
        Num,
    }

    let mut res: Vec<ExpToken> = Vec::new();
    let mut last_add_type = LastOpType::Init;
    let mut point_index_right: u32 = 0;
    for c in s.chars() {
        match c.to_digit(10) {
            Some(n) => {
                if last_add_type == LastOpType::Num {
                    let old_num = res.pop();
                    res.push(ExpToken::Opnum(
                        append_enum_num(&old_num, &(n as u8), point_index_right)?
                    ));
                } else {
                    res.push(ExpToken::Opnum(n as f64));
                }
                if point_index_right > 0 {
                    point_index_right = point_index_right + 1;
                }
                last_add_type = LastOpType::Num;
            },
            None => {
                if c == ' ' {
                    continue;
                }
                if c == '.' {
                    point_index_right = 1;
                } else {
                    res.push(ExpToken::Opchar(c));
                    point_index_right = 0;
                    last_add_type = LastOpType::Op;
                }
            }
        }
    }
    return Ok(res);
}

fn append_enum_num(old_num: &Option<ExpToken>, n: &u8, point_index_right: u32) -> Result<f64, String> {
    match old_num {
        Some(et) => {
            match et {
                ExpToken::Opchar(c) => {
                    Err(format!("expToken({}) must be num", c))
                },
                ExpToken::Opnum(num) => {
                    Ok(append_num_char(num, n, point_index_right))
                },
            }
        },
        None => {
            Ok(n.clone() as f64)
        }
    }
}

fn append_num_char(old_num: &f64, n: &u8, point_index_right: u32) -> f64 {
    if point_index_right > 0 {
        *old_num + *n as f64 * 10_f64.powi(0_i32 - point_index_right as i32) as f64
    } else {
        *old_num * 10_f64 + *n as f64
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_new() {
        let expression = Expression::new(String::from("123")).unwrap();
        assert_eq!(vec![ExpToken::Opnum(123_f64)], *expression.get());
    }

    #[test]
    fn test_append_num_char() {
        let new_num = append_num_char(&12.0_f64, &3_u8, 2);
        assert_eq!(new_num, 12.03_f64);
        let new_num = append_num_char(&12.0_f64, &3_u8, 1);
        assert_eq!(new_num, 12.3_f64);
        let new_num = append_num_char(&12_f64, &3_u8, 0);
        assert_eq!(new_num, 123_f64);
        let new_num = append_num_char(&1212_f64, &3_u8, 0);
        assert_eq!(new_num, 12123_f64);
    }

    #[test]
    fn test_append_enum_num() {
        let new_num = append_enum_num(&Some(ExpToken::Opnum(12.0_f64)), &3_u8, 2);
        assert_eq!(new_num, Ok(12.03_f64));
        let new_num = append_enum_num(&Some(ExpToken::Opnum(12.0_f64)), &3_u8, 1);
        assert_eq!(new_num, Ok(12.3_f64));
        let new_num = append_enum_num(&Some(ExpToken::Opnum(12_f64)), &3_u8, 0);
        assert_eq!(new_num, Ok(123_f64));
        let new_num = append_enum_num(&Some(ExpToken::Opnum(1212_f64)), &3_u8, 0);
        assert_eq!(new_num, Ok(12123_f64));
    }

    #[test]
    fn test_parse_exp() {
        let v1 = parse_exp(String::from("1+2")).unwrap();
        assert_eq!(v1, vec![ExpToken::Opnum(1.0), ExpToken::Opchar('+'), ExpToken::Opnum(2.0)]);
        let v1 = parse_exp(String::from("10+2")).unwrap();
        assert_eq!(v1, vec![ExpToken::Opnum(10.0), ExpToken::Opchar('+'), ExpToken::Opnum(2.0)]);
        let v1 = parse_exp(String::from("10.2+2")).unwrap();
        assert_eq!(v1, vec![ExpToken::Opnum(10.2), ExpToken::Opchar('+'), ExpToken::Opnum(2.0)]);
        let v1 = parse_exp(String::from("10.02+2")).unwrap();
        assert_eq!(v1, vec![ExpToken::Opnum(10.02), ExpToken::Opchar('+'), ExpToken::Opnum(2.0)]);
        let v1 = parse_exp(String::from("10.02+2^")).unwrap();
        assert_eq!(v1, vec![ExpToken::Opnum(10.02), ExpToken::Opchar('+'), ExpToken::Opnum(2.0), ExpToken::Opchar('^')]);
        let v1 = parse_exp(String::from("10.02 + 2 ^")).unwrap();
        assert_eq!(v1, vec![ExpToken::Opnum(10.02), ExpToken::Opchar('+'), ExpToken::Opnum(2.0), ExpToken::Opchar('^')]);
    }
}